#! /usr/bin/python
# -*- coding: utf-8 -*-
# @author HanYZ
# @createAt 2022.08.30
# 网易萧先生-鱼跃龙门

import backtrader as bt

class StrategyClass(bt.Strategy):
    '''鱼跃龙门'''

    params = dict(
        code = None,
        name = None,
        log = True,
        buyhook = None,
        thedays = None,
    )

    def buyhook(self, text):
        if self.p.buyhook:
            self.p.buyhook(code=self.p.code, name=self.p.name, date=self.datetime.datetime(), shape='longmen', shape_cn='鱼跃龙门', log=text)
        else:
            self.log(text)

    def log(self, txt, dt=None, force=False):
        if force or self.p.log:
            dt = dt or self.data.datetime.date()
            who = f'{self.p.code} {self.p.name} ' if self.p.code else ''
            print(f'{dt.isoformat()} {who}{txt}')

    def notify_order(self, order):
        if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
            return

        if order.status == order.Completed:
            if order.isbuy():
                buytxt = '买入价:%.2f, 量:%s, 持仓:%s' % (order.executed.price, order.executed.size, self.position.size)
                self.log(buytxt, bt.num2date(order.executed.dt))
            else:
                selltxt = '卖出价, %.2f, 量:%s, 持仓:%s' % (
                    order.executed.price, order.executed.size, self.position.size)
                if 'log' in order.info:
                    selltxt = '%s %s' % (selltxt, order.info.log)
                self.log(selltxt, bt.num2date(order.executed.dt))

        elif order.status in [order.Expired, order.Canceled, order.Margin]:
            if 'log' not in order.info:
                self.log('%s , %s' % (order.Status[order.status], order))
            pass

    def __init__(self):
        if self.p.thedays:
            return
        ma = lambda n: bt.ind.SMA(self.data, period=n)
        ma5, ma10, ma20, ma30, ma60 = ma(5), ma(10), ma(20), ma(30), ma(60)
        self.down = bt.And(ma30 > ma20, ma20 > ma10, ma10 > ma5, ma60 > ma5)
        self.red = self.data.close > self.data.open

        self.ma60 = ma60
        self.c5x10 = bt.ind.CrossOver(ma5, ma10)

        self.status = 0

    def next(self):
        if self.p.thedays:
            if self.datetime.datetime() in self.p.thedays:
                if self.p.buyprice > 0:
                    price = self.data.close[0] * self.p.buyprice
                    self.buy_bracket(
                        price=price,  oargs={"log": f"限价({price:.2f},{self.p.buyprice}%)买入"},
                        stopexec=bt.Order.StopTrail, stopargs={"trailpercent": self.p.trailpercent, "log": f"跟踪止损 {self.p.trailpercent}"},
                        limitprice=price * self.p.planstop, limitargs={"log": "计划({self.p.planstop}%)止盈"}
                    )
                else:
                    self.buy_bracket(
                        exectype=bt.Order.Market, oargs={"log": f"市价买入"},
                        stopexec=bt.Order.StopTrail, stopargs={"trailpercent": self.p.trailpercent, "log": f"跟踪止损 {self.p.trailpercent}"},
                        limitprice=self.data.close[0] * self.p.planstop, limitargs={"log": "计划({self.p.planstop}%)止盈"}
                    )
        elif self.status == 0 and self.down:
            self.status = 1
            self.red1_top = -1
            self.low_top = -1
            self.low = -1
            self.log(f'下降排列 ')
        elif self.status == 1:
            if self.red1_top > 0:
                if self.data.high[0] > self.low_top:
                    self.log(f'底部待确认新顶，旧顶：{self.red1_top:.2f}，新顶：{self.data.high[0]}')
                    self.low_top = self.data.high[0]
                if self.low > self.data.low[0]:
                    self.log(f'底部新低，旧底：{self.low:.2f}，新底：{self.data.low[0]}')
                    self.low = self.data.low[0]
                    if self.low_top > self.red1_top:
                        self.log(f'确认底部新顶，旧顶：{self.red1_top:.2f}，新顶：{self.low_top:.2f}')
                        self.red1_top = self.low_top
                if self.c5x10 > 0:
                    self.status = 2
                    if self.low_top > self.red1_top:
                        self.log(f'确认底部新顶，旧顶：{self.red1_top:.2f}，新顶：{self.low_top:.2f}')
                        self.red1_top = self.low_top
            elif self.red:
                self.red1_top = self.low_top = self.data.high[0]
                self.low = self.data.low[0]
                self.log(f'遇到红柱，开始底部观察，top：{self.red1_top:.2f}')
        elif self.status == 2 and self.data.close[0] > self.red1_top > 0:
            self.log('鱼跃龙门', force=True)
            self.status = 0

EasyNetLongMen = StrategyClass
